package org.ccay.enumerable.mybatis;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Statement;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.invoker.Invoker;
import org.apache.ibatis.session.SqlSessionFactory;
import org.ccay.core.ioc.Ccay;
import org.ccay.enumerable.EnumItem;
import org.ccay.enumerable.EnumUtil;
import org.ccay.enumerable.exception.NoEnumTypeFoundException;

@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) })
public class EnumItemResultSetsInterceptor implements Interceptor{

	private ReflectorFactory reflectorFactory;
	
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		List result = (List) invocation.proceed();
		if(result != null ){
			for(Object item : result){
				findAndReplaceEnumItem(item);
			}
		}
		return result;
	}

	protected void findAndReplaceEnumItem(Object item) throws IllegalAccessException, InvocationTargetException, NoSuchFieldException, SecurityException {
		MetaClass metaClass = MetaClass.forClass(item.getClass(), getReflectorFactory());
		String[] setterNames = metaClass.getSetterNames();
		for(String setter : setterNames){
			if(metaClass.getSetterType(setter).equals(EnumItem.class)){
				Object value = metaClass.getGetInvoker(setter).invoke(item, null);
				if(value !=null && value.getClass().equals(JustValueEnumItem.class)){
					ParameterizedType type = (ParameterizedType) item.getClass().getDeclaredField(setter).getGenericType();
					Type[] actualTypes = type.getActualTypeArguments();
					if(actualTypes == null || actualTypes.length!=1){
						throw new NoEnumTypeFoundException();
					}
					Class<?> enumItemType = (Class<?>) type.getActualTypeArguments()[0];
					Invoker setterInvoker = metaClass.getSetInvoker(setter);
					EnumItem<?> enumItem = EnumUtil.getInstance().getByTypeValue(enumItemType.getSimpleName(),((JustValueEnumItem)value).getValue());
					setterInvoker.invoke(item,new Object[]{enumItem});
				}
			}
		}
	}

	
	private ReflectorFactory getReflectorFactory(){
		if(reflectorFactory == null){
			reflectorFactory = Ccay.getContext().getBean(SqlSessionFactory.class).getConfiguration().getReflectorFactory();
		}
		return reflectorFactory;
	}
	
	
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
	}
	
	
}
